package outbound.http;

import common.NamedThreadFactory;
import filter.HttpRequestFilter;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import model.HttpConfig;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import router.MyHttpRouter;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.*;

import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

/**
 * @author dsp
 * @date 2021-04-04
 */
public class HobHandler {

    private HttpConfig httpConfig;
    private ExecutorService backendWorkerService;
    private List<String> backendUrls;
    private OkHttpClient okHttpClient;
    private MyHttpRouter router;


    public HobHandler(HttpConfig httpConfig) {
        this.httpConfig = httpConfig;
        router = new MyHttpRouter(httpConfig);

        // TODO 对请求url做格式化，自定规则
        //  如果是微服务，就是要按需截取前缀，保留剩下的请求路径与后端服务IP拼到一起
        this.backendUrls = backendUrls;

        // thread pool
        int cores = Runtime.getRuntime().availableProcessors();
        long keepAliveTime = 1000;
        int queueSize = 2048;
        RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
        backendWorkerService = new ThreadPoolExecutor(cores, cores,
                keepAliveTime, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queueSize),
                new NamedThreadFactory("proxyService"), handler);

        // okhttp
        okHttpClient = new OkHttpClient.Builder()
                .followRedirects(true)
                .connectTimeout(2, TimeUnit.SECONDS)
                .readTimeout(2, TimeUnit.SECONDS)
                .connectionPool(new ConnectionPool(5, 2, TimeUnit.SECONDS))
                .writeTimeout(1, TimeUnit.SECONDS)
                .build();
    }

    /**
     * TODO 路由处理，负载均衡，分发到指定服务
     */
    public void handle(final FullHttpRequest fullRequest, final ChannelHandlerContext ctx, List<HttpRequestFilter> filters) {
        String backendHost = router.route(router.findRouter(fullRequest.uri()));
        // 返回一个后端服务的地址
        final String url = backendHost + getPath(fullRequest.uri());
        filters.forEach(e -> e.filter(fullRequest, ctx));
        backendWorkerService.submit(() -> fetchGet(fullRequest, ctx, url));
    }

    /**
     * TODO 提取path
     */
    private String getPath(String uri) {
        return "/test";
    }

    /**
     * TODO 请求指定的服务获取结果，由于没有真实的后端方服务,这里不做调用，后面先随便拼一个结果
     */
    private void fetchGet(final FullHttpRequest inbound, final ChannelHandlerContext ctx, final String url) {
        String method = inbound.method().name();
        if (HttpMethod.GET.name().equals(method)) {

        } else if (HttpMethod.POST.name().equals(method)) {

        } else if (HttpMethod.DELETE.name().equals(method)) {

        } else if (HttpMethod.PUT.name().equals(method)) {

        } else if (HttpMethod.PATCH.name().equals(method)) {

        }
        // Request request = new Request.Builder()
        //         .url(url)
        //         .build();
        // try (Response response = okHttpClient.newCall(request).execute()) {
        //     System.out.println(Objects.requireNonNull(response.body()).string());
        // } catch (IOException e) {
        //     e.printStackTrace();
        // }
        FullHttpResponse response = null;
        try {
            byte[] body = "ABCDEFG".getBytes(StandardCharsets.UTF_8);
            response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(body));
            response.headers().set("Content-Type", "application/json");
            response.headers().setInt("Content-Length", body.length);
        } catch (Exception e) {
            e.printStackTrace();
            response = new DefaultFullHttpResponse(HTTP_1_1, NO_CONTENT);
        } finally {
            if (inbound != null) {
                if (!HttpUtil.isKeepAlive(inbound)) {
                    ctx.write(response).addListener(ChannelFutureListener.CLOSE);
                } else {
                    ctx.write(response);
                }
            }
            ctx.flush();
            ctx.close();
        }

    }

}
